﻿SimpleILParser = function ()
{
    
};

SimpleILParser.prototype.Assemble = function (input) {
    var rawCommands = input.trim().split("\n");
    var cleanedInput = [];
    
    for (var i = 0; i < rawCommands.length; i++) {
        var candidate = rawCommands[i].trim();
        if (candidate && candidate.length > 0)
            cleanedInput[cleanedInput.length] = candidate;
    }
    
    var labelsRemoved = [];
    var labelIndexes = {};

    var numLabels = 0;
    
    for (var j = 0; j < cleanedInput.length; j++)
    {    
        var label = this.getLabel(cleanedInput[j]);
        if (!label)
            labelsRemoved[labelsRemoved.length] = cleanedInput[j];
        else
        {
            if (labelIndexes[label])
                throw "DuplicateLabelException";
            
            labelIndexes[label] = j - numLabels;
            numLabels++;
        }
    }
    
    var finalOutput = [];
    
    // loop through to find branches and replace their label values with offsets
    for (var k = 0; k < labelsRemoved.length; k++)
    {
        var branchCandidate = labelsRemoved[k];
        
        if (this.isBranchCommand(branchCandidate))
            finalOutput[k] = this.replaceBranchCommand(branchCandidate, k, labelIndexes);
        else
            finalOutput[k] = branchCandidate;    
    }        

    return finalOutput;                                                     
};          

SimpleILParser.prototype.replaceBranchCommand = function (branchCommand, sourceIndex, labelHash)
{
    var parts = branchCommand.split(" ");
    if (!parts || parts.length <= 1)
        throw "InvalidBranchCommandException";
    
    var labelName= parts[parts.length - 1];
    
    if (!isNaN(labelName))
        return branchCommand;
    
    
    var indexValue = labelHash[labelName];
    
    if (indexValue == null)
        throw "LabelNotFoundException";
    
    var labelIndex = labelHash[labelName];
    var offset = labelIndex - sourceIndex - 1;    
    parts[parts.length - 1] = offset;
    
    return parts.join(" ");    
};

SimpleILParser.prototype.getLabel = function (command)
{
    if (command.substring(command.length - 1) != ":")
        return null;
    
    var remaining = command.substring(0, command.length - 1);
    if (remaining.length == 0)
        throw "InvalidLabelException";
    
    remaining = remaining.trim();
    if (remaining.length == 0)
        throw "InvalidLabelException";
    
    var parts = command.split(" ");
    if (parts.length > 1)
        throw "InvalidLabelException";
    
    return remaining;    
};

SimpleILParser.prototype.isBranchCommand = function (command)
{
    var commandParts = command.split(" ");
    if (commandParts.length <= 1)
        return false;
    
    var commandName = commandParts[0];
    
    switch (commandName)
    {
        case "beq":
        case "beq.s":
        
        case "bge":
        case "bge.s":
        case "bge.un":
        case "bge.un.s":
        
        case "bgt":
        case "bgt.s":
        case "bgt.un":
        case "bgt.un.s":
        
        case "ble":
        case "ble.s":
        case "ble.un":
        case "ble.un.s":
        
        case "blt":
        case "blt.s":
        case "blt.un":
        case "blt.un.s":
       
        case "bne.un":
        case "bne.un.s":
        
        case "br":
        case "br.s":
        
        case "brfalse":
        case "brfalse.s":
        case "brtrue":
        case "brtrue.s":
        
            return true;
        
        default :
            return false;
    }
};
